﻿#region

using System;
using System.Collections.Generic;
using System.Linq;

#endregion

namespace Tum.CollabXT
{
    /// <summary>
    /// Collection of helper functions. Injected into IProcessProvider via extension methods.
    /// </summary>
    public static class AssociationTools
    {
        /// <summary>
        /// Gets a artifact by its id.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="id">Id of the artifact to be returned.</param>
        /// <returns>Artifact having the passed id or null if it does not exist.</returns>
        public static IArtifact GetArtifactByID(this IProcessProvider pp, string id)
        {
            return GetProcessObjectByID(id, pp.Artifacts);
        }

        /// <summary>
        /// Gets an activity by its id.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="id">Id of the activity to be returned.</param>
        /// <returns>Activity having the passed id or null if it does not exist.</returns>
        public static IActivity GetActivityByID(this IProcessProvider pp, string id)
        {
            return GetProcessObjectByID(id, pp.Activities);
        }

        /// <summary>
        /// Gets a task by its id.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="id">Id of the task to be returned.</param>
        /// <returns>Task having the passed id or null if it does not exist.</returns>
        public static ITask GetTaskByID(this IProcessProvider pp, string id)
        {
            return GetProcessObjectByID(id, pp.Tasks);
        }

        /// <summary>
        /// Gets a milestone by its id.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="id">Id of the milestone to be returned.</param>
        /// <returns>Milestone having the passed id or null if it does not exist.</returns>
        public static IMilestone GetMilestoneByID(this IProcessProvider pp, string id)
        {
            return GetProcessObjectByID(id, pp.Milestones);
        }

        /// <summary>
        /// Gets a topic by its id.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="id">Id of the topic to be returned.</param>
        /// <returns>Topic having the passed id or null if it does not exist.</returns>
        public static ITopic GetTopicByID(this IProcessProvider pp, string id)
        {
            return GetProcessObjectByID(id, pp.Topics);
        }

        /// <summary>
        /// Gets a discipline by its id.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="id">Id of the discipline to be returned.</param>
        /// <returns>Discipline having the passed id or null if it does not exist.</returns>
        public static IDiscipline GetDisciplineByID(this IProcessProvider pp, string id)
        {
            return GetProcessObjectByID(id, pp.Disciplines);
        }

        /// <summary>
        /// Gets a role by its id.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="id">Id of the role to be returned.</param>
        /// <returns>Role having the passed id or null if it does not exist.</returns>
        public static IRole GetRoleByID(this IProcessProvider pp, string id)
        {
            return GetProcessObjectByID(id, pp.Roles);
        }

        private static T GetProcessObjectByID<T>(string id, IEnumerable<T> objList) where T : class, IStaticProcessElement
        {
            foreach (T curObj in objList)
            {
                if (curObj.Id.Equals(id, StringComparison.OrdinalIgnoreCase))
                    return curObj;
            }
            return null;
        }


        /// <summary>
        /// Gets all responsible roles by an artifact.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="artifact">Artifact.</param>
        /// <returns>Array of roles that are responsible for the artifact having the passed id.</returns>
        public static IRole[] GetRolesResponsibleByArtifact(this IProcessProvider pp, IArtifact artifact)
        {
            return GetRolesByArtifact<RoleIsResponsibleForArtifact>(pp, artifact);
        }

        /// <summary>
        /// Gets all participating roles by an artifact.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="artifact">Artifact.</param>
        /// <returns>Array of roles that are participating in the artifact having the passed id.</returns>
        public static IRole[] GetRolesParticipatingByArtifact(this IProcessProvider pp, IArtifact artifact)
        {
            return GetRolesByArtifact<RoleIsParticipatingInArtifact>(pp, artifact);
        }

        private static IRole[] GetRolesByArtifact<T>(IProcessProvider pp, IArtifact artifact) where T : RoleToArtifact
        {
            var responsibleRoles = new List<IRole>();
            foreach (IAssociation curDep in pp.Associations)
            {
                if (curDep is T)
                {
                    if ((curDep as T).Destination.Equals(artifact))
                        responsibleRoles.Add((curDep as T).Source);
                }
            }
            return responsibleRoles.ToArray<IRole>();
        }

        /// <summary>
        /// Gets all artifacts that a certain role is responsible for.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="role">Responsible role.</param>
        /// <returns>Array of artifacts that the passed role is responsible for.</returns>
        public static IArtifact[] GetArtifactsByResponsibleRole(this IProcessProvider pp, IRole role)
        {
            return GetArtifactsByRole<RoleIsResponsibleForArtifact>(pp, role);
        }

        /// <summary>
        /// Gets all artifacts that a certain role is participating in.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="role">Participating role.</param>
        /// <returns>Array of artifacts that the passed role is participating in.</returns>
        public static IArtifact[] GetArtifactsByParticipatingRole(this IProcessProvider pp, IRole role)
        {
            return GetArtifactsByRole<RoleIsParticipatingInArtifact>(pp, role);
        }

        private static IArtifact[] GetArtifactsByRole<T>(IProcessProvider pp, IRole role) where T : RoleToArtifact
        {
            var artifacts = new List<IArtifact>();
            foreach (IAssociation curDep in pp.Associations)
            {
                if (curDep is T)
                {
                    if ((curDep as T).Source.Equals(role))
                        artifacts.Add((curDep as T).Destination);
                }
            }
            return artifacts.ToArray<IArtifact>();
        }

        /// <summary>
        /// Gets all milestones belonging to a certain artifact.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="artifact">Artifact.</param>
        /// <returns>Array of milestones belonging to the artifact.</returns>
        public static IMilestone[] GetMilestonesByArtifact(this IProcessProvider pp, IArtifact artifact)
        {
            var milestones = new List<IMilestone>();
            foreach (IAssociation curDep in pp.Associations)
            {
                if (curDep is ArtifactToMilestone)
                {
                    if ((curDep as ArtifactToMilestone).Source.Equals(artifact))
                        milestones.Add((curDep as ArtifactToMilestone).Destination);
                }
            }
            return milestones.ToArray<IMilestone>();
        }

        /// <summary>
        /// Gets all artifacts belonging to a certain milestone.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="milestone">Milestone.</param>
        /// <returns>Array of artifacts belonging to the milestone.</returns>
        public static IArtifact[] GetArtifactsByMilestone(this IProcessProvider pp, IMilestone milestone)
        {
            var artifacts = new List<IArtifact>();
            foreach (IAssociation curDep in pp.Associations)
            {
                if (curDep is ArtifactToMilestone)
                {
                    if ((curDep as ArtifactToMilestone).Destination.Equals(milestone))
                        artifacts.Add((curDep as ArtifactToMilestone).Source);
                }
            }
            return artifacts.ToArray<IArtifact>();
        }


        /// <summary>
        /// Gets all activities belonging to a certain artifact.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="artifact">Artifact.</param>
        /// <returns>Array of activities belonging to the artifact.</returns>
        public static IActivity[] GetActivitiesByArtifact(this IProcessProvider pp, IArtifact artifact)
        {
            var activities = new List<IActivity>();
            foreach (IAssociation curDep in pp.Associations)
            {
                if (curDep is ActivityToArtifact)
                {
                    if ((curDep as ActivityToArtifact).Destination.Equals(artifact))
                        activities.Add((curDep as ActivityToArtifact).Source);
                }
            }
            return activities.ToArray<IActivity>();
        }

        /// <summary>
        /// Gets all artifacts belonging to a certain activity.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="activity">Activity.</param>
        /// <returns>Array of artifacts belonging to the activity.</returns>
        public static IArtifact[] GetArtifactsByActivity(this IProcessProvider pp, IActivity activity)
        {
            var artifacts = new List<IArtifact>();
            foreach (IAssociation curDep in pp.Associations)
            {
                if (curDep is ActivityToArtifact)
                {
                    if ((curDep as ActivityToArtifact).Source.Equals(activity))
                        artifacts.Add((curDep as ActivityToArtifact).Destination);
                }
            }
            return artifacts.ToArray<IArtifact>();
        }

        /// <summary>
        /// Gets all artifacts that depend on a certain artifact's content.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="artifact">Artifact.</param>
        /// <returns>Array of artifacts depending on the artifact's content.</returns>
        public static IArtifact[] GetContentDependentArtifactsByArtifact(this IProcessProvider pp, IArtifact artifact)
        {
            return GetDependentArtifactsByArtifact<ContentDependency>(pp, artifact);
        }

        /// <summary>
        /// Gets all artifacts that depend on a certain artifact's creation.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="artifact">Artifact.</param>
        /// <returns>Array of artifacts depending on the artifact's creation.</returns>
        public static IArtifact[] GetGeneratedArtifactsByArtifact(this IProcessProvider pp, IArtifact artifact)
        {
            return GetDependentArtifactsByArtifact<GenerationDependency>(pp, artifact);
        }

        private static IArtifact[] GetDependentArtifactsByArtifact<T>(IProcessProvider pp, IArtifact artifact) where T : ArtifactToArtifact
        {
            var artifacts = new List<IArtifact>();
            foreach (IAssociation curDep in pp.Associations)
            {
                if (curDep is T)
                {
                    var curProdDependency = curDep as T;
                    if (curProdDependency.Source.Equals(artifact))
                        artifacts.Add(curProdDependency.Destination);
                }
            }
            return artifacts.ToArray<IArtifact>();
        }


        public static IAssociation GetAssociation<TSource, TDestination>(this IProcessProvider pp, TSource source, TDestination destination)
            where TSource : class, IProcessElement where TDestination : class, IProcessElement
        {
            var assocs = GetAssociations(pp, source, destination);
            return assocs.Length == 0 ? null : assocs[0];
        }

        public static IAssociation[] GetAssociations<TSource, TDestination>(this IProcessProvider pp, TSource source, TDestination destination)
            where TSource : class, IProcessElement
            where TDestination : class, IProcessElement
        {
            var foundAssocs = new List<IAssociation>();
            foreach (IAssociation curDep in pp.Associations)
            {
                if (curDep.SourceType.Equals(typeof(TSource)) && curDep.DestinationType.Equals(typeof(TDestination)))
                {
                    var assoc = curDep as Association<TSource, TDestination>;
                    if (assoc == null)
                        continue;
                    if (assoc.Source.Equals(source) && assoc.Destination.Equals(destination))
                        foundAssocs.Add(assoc);
                }
            }
            return foundAssocs.ToArray();
        }

        public static IAssociation[] GetAssociationsWithSource<TSource, TDestination>(this IProcessProvider pp, TSource source)
            where TSource : class, IProcessElement
            where TDestination : class, IProcessElement
        {
            var foundAssocs = new List<IAssociation>();
            foreach (IAssociation curDep in pp.Associations)
            {
                if (curDep.SourceType.Equals(typeof(TSource)) && curDep.DestinationType.Equals(typeof(TDestination)))
                {
                    var assoc = curDep as Association<TSource, TDestination>;
                    if (assoc == null)
                        continue;
                    if (assoc.Source.Equals(source))
                        foundAssocs.Add(assoc);
                }
            }
            return foundAssocs.ToArray();
        }

        public static IAssociation[] GetAssociationsWithDestination<TSource, TDestination>(this IProcessProvider pp, TDestination destination)
            where TSource : class, IProcessElement
            where TDestination : class, IProcessElement
        {
            var foundAssocs = new List<IAssociation>();
            foreach (IAssociation curDep in pp.Associations)
            {
                if (curDep.SourceType.Equals(typeof(TSource)) && curDep.DestinationType.Equals(typeof(TDestination)))
                {
                    var assoc = curDep as Association<TSource, TDestination>;
                    if (assoc == null)
                        continue;
                    if (assoc.Destination.Equals(destination))
                        foundAssocs.Add(assoc);
                }
            }
            return foundAssocs.ToArray();
        }


        /// <summary>
        /// Gets all topics belonging to a certain artifact.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="artifact">Artifact id.</param>
        /// <returns>Array of topics belonging to the artifact.</returns>
        public static ITopic[] GetTopicsByArtifact(this IProcessProvider pp, IArtifact artifact)
        {
            var topics = new List<ITopic>();
            foreach (IAssociation curDep in pp.Associations)
            {
                if (curDep is TopicToArtifact)
                {
                    var curPtDep = curDep as TopicToArtifact;
                    if (curPtDep.Destination.Equals(artifact))
                        topics.Add(curPtDep.Source);
                }
            }
            topics.Sort((a, b) => a.Number.CompareTo(b.Number));
            return topics.ToArray();
        }

        /// <summary>
        /// Gets all artifacts belonging to a certain topic.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="topic">Topic.</param>
        /// <returns>Array of artifacts belonging to the topic.</returns>
        public static IArtifact[] GetArtifactsByTopic(this IProcessProvider pp, ITopic topic)
        {
            var artifacts = new List<IArtifact>();
            foreach (IAssociation curDep in pp.Associations)
            {
                if (curDep is TopicToArtifact)
                {
                    if ((curDep as TopicToArtifact).Source.Equals(topic))
                        artifacts.Add((curDep as TopicToArtifact).Destination);
                }
            }
            return artifacts.ToArray<IArtifact>();
        }


        /// <summary>
        /// Gets all artifacts belonging to a certain discipline.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="discipline">Discipline.</param>
        /// <returns>Array of artifacts belonging to the discipline.</returns>
        public static IArtifact[] GetArtifactsByDiscipline(this IProcessProvider pp, IDiscipline discipline)
        {
            var artifacts = new List<IArtifact>();
            foreach (IAssociation curDep in pp.Associations)
            {
                if (curDep is ArtifactToDiscipline)
                {
                    if ((curDep as ArtifactToDiscipline).Destination.Equals(discipline))
                        artifacts.Add((curDep as ArtifactToDiscipline).Source);
                }
            }
            return artifacts.ToArray<IArtifact>();
        }

        /// <summary>
        /// Gets all child topics of a certain topic.
        /// </summary>
        /// <param name="pp">IProcessProvider. For extension method usage.</param>
        /// <param name="topic">Topic.</param>
        /// <returns>Array of child topics.</returns>
        public static ITopic[] GetDirectSubTopics(this IProcessProvider pp, ITopic topic)
        {
            var subTopics = new List<ITopic>();
            foreach (IAssociation curDep in pp.Associations)
            {
                if (curDep is TopicToSubTopic)
                {
                    if ((curDep as TopicToSubTopic).Source.Equals(topic))
                        subTopics.Add((curDep as TopicToSubTopic).Destination);
                }
            }
            return subTopics.ToArray<ITopic>();
        }
    }
}